<template>
  <div class="image-upload-component">
    <el-upload
      :action="action"
      list-type="picture-card"
      :auto-upload="autoUpload"
      :limit="limit"
      :on-preview="handlePictureCardPreview"
      :on-change="handleImageChange"
      :on-remove="handleImageRemove"
      :on-exceed="handleImageExceed"
      :on-success="handleUploadSuccess"
      :disabled="disabled"
      :accept="accept"
      :before-upload="beforeUploadHandler"
      :file-list="fileList"
      :drag="drag"
      :http-request="customHttpRequestHandler"
      :multiple="multiple"
      v-bind="$attrs"
      ref="uploadRef"
    >
      <template #default>
        <div v-if="drag">
          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
          <div class="el-upload__text">
            将文件拖到此处，或<em>点击上传</em>
          </div>
        </div>
        <el-icon v-else><Plus /></el-icon>
      </template>
      
      <template #file="{ file }" v-if="$slots.file">
        <slot name="file" :file="file"></slot>
      </template>
      
      <template #tip>
        <div class="el-upload__tip" :style="tipTextStyle">
          {{ tipText }}
        </div>
      </template>
    </el-upload>
    
    <el-image-viewer
      v-if="previewVisible"
      :url-list="[previewUrl]"
      :zoom-rate="1.2"
      :initial-index="0"
      :infinite="false"
      show-progress
      @close="previewVisible = false"
    />
  </div>
</template>

<script setup>
import { ref, defineProps, defineEmits, computed, watch, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus, UploadFilled } from '@element-plus/icons-vue';

// 导入工具模块
import { beforeImageUpload } from './utils/fileValidator';
import { convertFileToBase64 } from './utils/fileConverter';
import { compressImage } from './utils/imageCompressor';
import { 
  detectInitialDataFormat, 
  formatOutputData, 
  getFileList as getFileListHelper, 
  processInputValue, 
  getFileUrls as getFileUrlsHelper 
} from './utils/formatHelper';
import { customHttpRequest } from './utils/uploader';

const props = defineProps({
  // 上传地址
  action: {
    type: String,
    default: '#'
  },
  // 是否自动上传
  autoUpload: {
    type: Boolean,
    default: true
  },
  // 舒服支持多选
  multiple: {
    type: Boolean,
    default: false
  },
  // 是否使用表单提交模式
  useFormData: {
    type: Boolean,
    default: true
  },
  // 文件参数名
  name: {
    type: String,
    default: 'file'
  },
  // 上传时附带的额外参数
  data: {
    type: Object,
    default: () => ({})
  },
  // 上传文件数量限制
  limit: {
    type: Number,
    default: 5
  },
  // 是否禁用
  disabled: {
    type: Boolean,
    default: false
  },
  // 是否启用拖拽上传
  drag: {
    type: Boolean,
    default: false
  },
  // 接受的文件类型
  accept: {
    type: String,
    default: '.jpg,.jpeg,.png'
  },
  // 文件大小限制（MB）
  maxSize: {
    type: Number,
    default: 20
  },
  // 压缩后的目标大小（MB）
  compressSize: {
    type: Number,
    default: 5
  },
  // 是否启用压缩
  enableCompress: {
    type: Boolean,
    default: true
  },
  // 压缩质量 (0-1)
  compressQuality: {
    type: Number,
    default: 1
  },
  // 最大压缩尝试次数
  maxCompressTries: {
    type: Number,
    default: 5
  },
  // 提示文本
  tipText: {
    type: String,
    default: '仅限jpg、jpeg、png格式，单张图片不超过20MB，大于5MB的图片将会自动压缩'
  },
  // 提示文本颜色
  tipTextColor: {
    type: String,
    default: '#909399'
  },
  // 提示文本字体大小
  tipTextSize: {
    type: String,
    default: '12px'
  },
  // 提示文本样式对象（优先级高于单独的样式属性）
  tipTextStyle: {
    type: Object,
    default: () => ({})
  },
  // 数据格式类型 ('string': 字符串数组, 'object': 对象数组, 'auto': 自动检测并保持一致)
  dataFormat: {
    type: String,
    default: 'auto',
    validator: (value) => ['auto', 'object', 'string'].includes(value)
  },
  // 自定义上传请求方法
  httpRequest: {
    type: Function,
    default: null
  },
  // 缩略图缩放模式
  imageScale: {
    type: String,
    default: 'contain',
    validator: (value) => ['contain', 'cover', 'fill', 'none', 'scale-down'].includes(value)
  },
  // 文件列表
  modelValue: {
    type: Array,
    default: () => []
  }
});

const emit = defineEmits(['update:modelValue', 'change', 'remove', 'exceed', 'preview', 'error', 'urlsChange']);

// 记录初始输入类型，用于auto模式
const initialDataFormat = ref('object');

// 强制检查现有文件列表中的大小
const checkExistingFiles = () => {
  if (props.modelValue && props.modelValue.length > 0) {
    // 检查现有文件大小
    const oversizedFiles = props.modelValue.filter(file => {
      if (file.size) {
        const sizeMB = file.size / 1024 / 1024;
        return sizeMB >= props.maxSize;
      }
      return false;
    });
    
    // 如果有超出大小的文件，发出警告
    if (oversizedFiles.length > 0) {
      ElMessage.warning(`检测到${oversizedFiles.length}个文件超过${props.maxSize}MB大小限制，请删除后重新上传`);
      console.warn('超出大小限制的文件:', oversizedFiles);
    }
  }
};

// 在组件挂载时检查文件大小和初始数据类型
onMounted(() => {
  console.log('ImageUpload组件挂载完成，配置:', {
    maxSize: props.maxSize,
    compressSize: props.compressSize,
    enableCompress: props.enableCompress,
    limit: props.limit,
    dataFormat: props.dataFormat
  });
  
  checkExistingFiles();
  
  // 检测初始数据类型
  initialDataFormat.value = detectInitialDataFormat(props.modelValue);
  console.log('检测到初始数据格式类型:', initialDataFormat.value);
  
  // 处理初始值
  handleModelValueChange(props.modelValue);
});

// 监听modelValue的变化，处理不同格式的输入
watch(() => props.modelValue, (newVal) => {
  handleModelValueChange(newVal);
});

// 处理输入值的变化，支持URL字符串数组
const handleModelValueChange = (value) => {
  processInputValue(value, props.dataFormat, initialDataFormat.value, (formattedData) => {
    emit('update:modelValue', formattedData);
  });
};

// 文件列表
const fileList = computed({
  get: () => {
    // 使用getFileList确保总是返回对象数组格式
    return getFileList();
  },
  set: (val) => {
    console.log('fileList.set被调用，文件数量:', val?.length);
    
    // 不再在此处进行文件大小验证，已在 beforeImageUpload 完成
    // 直接更新文件列表
    emit('update:modelValue', val);
  }
});

// 安全获取文件列表的辅助函数
const getFileList = () => {
  return getFileListHelper(props.modelValue);
};

// 计算最终的提示文本样式
const tipTextStyle = computed(() => {
  // 如果提供了完整的样式对象，则直接使用
  if (Object.keys(props.tipTextStyle).length > 0) {
    return props.tipTextStyle;
  }
  
  // 否则使用单独设置的样式属性
  return {
    color: props.tipTextColor,
    fontSize: props.tipTextSize
  };
});

// 预览相关
const previewVisible = ref(false);
const previewUrl = ref('');

// 预览图片
const handlePictureCardPreview = (file) => {
  previewUrl.value = file.url || URL.createObjectURL(file.raw);
  previewVisible.value = true;
  emit('preview', file);
};

// 上传前验证处理函数
const beforeUploadHandler = (file) => {
  return beforeImageUpload(file, {
    accept: props.accept,
    maxSize: props.maxSize
  });
};

// 自定义上传请求处理函数
const customHttpRequestHandler = (options) => {
  console.log('customHttpRequestHandler 被调用, 文件名:', options.file?.name);
  
  // 确保文件状态不是已经成功
  if (options.file && options.file.status === 'success' && options.file.url && options.file.url.startsWith('http')) {
    console.log('文件已经上传成功，URL:', options.file.url);
    // 模拟成功回调，保持与el-upload交互一致
    setTimeout(() => {
      options.onSuccess({ code: 200, data: options.file.url });
    }, 100);
    
    // 返回一个带abort方法的对象
    return {
      abort() {
        console.log('终止已完成的上传');
      }
    };
  }
  
  return customHttpRequest(options, {
    useFormData: props.useFormData,
    name: props.name,
    data: props.data,
    enableCompress: props.enableCompress,
    compressSize: props.compressSize,
    maxSize: props.maxSize,
    compressQuality: props.compressQuality,
    maxCompressTries: props.maxCompressTries,
    httpRequest: props.httpRequest,
    getFileList: getFileList,
    updateModelValue: (data) => emit('update:modelValue', data),
    formatOutputData: (fileList) => formatOutputData(fileList, props.dataFormat, initialDataFormat.value),
    emitUrlsChange: (urls) => emit('urlsChange', urls)
  });
};

// 处理图片变化
const handleImageChange = async (file, fileList) => {
  console.log('image-upload - handleImageChange 开始处理:', file?.name, fileList?.length);
  
  // 确保file对象存在
  if (!file) {
    console.warn('文件对象为空');
    emit('change', null, fileList);
    return;
  }

  // 如果文件带有被拒绝标记，直接跳过处理
  if (file._rejected) {
    console.log('文件已被beforeImageUpload拒绝，不再处理:', file.name);
    // 从文件列表中移除这个文件
    const filteredFileList = Array.isArray(fileList) ? fileList.filter(f => f.uid !== file.uid) : [];
    emit('update:modelValue', formatOutputData(filteredFileList, props.dataFormat, initialDataFormat.value));
    emit('change', file, filteredFileList);
    return;
  }

  // 如果文件已经上传成功，不再处理
  if (file.status === 'success' && file.url && file.response) {
    console.log('文件已上传成功，不再处理:', file.name, file.url);
    emit('change', file, fileList);
    return;
  }

  // 保存当前fileList的引用，以防在异步操作中被修改
  const currentFileList = Array.isArray(fileList) ? [...fileList] : [];
  
  // 只处理新上传的文件
  if (file && file.raw && file.status === 'ready') {
    // 再次检查文件大小（双重保险）
    if (file.raw.size / 1024 / 1024 >= props.maxSize) {
      console.log('handleImageChange检测到文件大小超过限制，跳过处理:', file.name);
      
      // 从文件列表中移除这个文件
      const updatedFileList = currentFileList.filter(f => f.uid !== file.uid);
      emit('update:modelValue', formatOutputData(updatedFileList, props.dataFormat, initialDataFormat.value));
      emit('change', file, updatedFileList);
      return;
    }
    
    // 检查文件格式
    const isJPG = file.raw.type === 'image/jpeg';
    const isPNG = file.raw.type === 'image/png';
    
    if (!isJPG && !isPNG) {
      ElMessage.error(`图片只能是 JPG 或 PNG 格式!`);
      emit('error', new Error(`图片只能是 JPG 或 PNG 格式!`), file);
      // 从文件列表中移除这个文件
      const updatedFileList = currentFileList.filter(f => f.uid !== file.uid);
      const formattedData = formatOutputData(updatedFileList, props.dataFormat, initialDataFormat.value);
      emit('update:modelValue', formattedData);
      emit('change', file, updatedFileList);
      return;
    }
    
    // 获取文件大小（仅用于日志和压缩判断，不做验证）
    const fileSizeMB = file.raw.size / 1024 / 1024;
    
    // 再次确认文件大小不超过限制
    if (fileSizeMB >= props.maxSize) {
      console.log('文件大小超过限制，跳过后续处理:', file.name);
      
      // 从文件列表中移除这个文件
      const updatedFileList = currentFileList.filter(f => f.uid !== file.uid);
      emit('update:modelValue', formatOutputData(updatedFileList, props.dataFormat, initialDataFormat.value));
      emit('change', file, updatedFileList);
      return;
    }
    
    const needCompress = props.enableCompress && fileSizeMB > props.compressSize;
    
    console.log('文件检查:', file.name, '大小:', fileSizeMB.toFixed(2), 'MB', '限制:', props.maxSize, 'MB', 
      '需要压缩:', needCompress, '(启用压缩:', props.enableCompress, ', 压缩阈值:', props.compressSize, 'MB)');

    // 确保上传前将文件状态设置为上传中，防止意外移除
    file.status = 'uploading';
    
    // 立即更新文件列表状态
    const updatedFileList = [...currentFileList];
    const fileIndex = updatedFileList.findIndex(f => f.uid === file.uid);
    if (fileIndex !== -1) {
      updatedFileList[fileIndex] = { ...file };
    } else {
      updatedFileList.push({ ...file });
    }
    emit('update:modelValue', updatedFileList);
    
    try {
      // 需要压缩的情况
      if (needCompress) {
        // 显示压缩中提示
        const loadingMsg = ElMessage({
          message: '图片压缩中，请稍候...',
          type: 'info',
          duration: 0
        });
        
        try {
          console.log('调用图片压缩方法 - 条件满足:', 
            '启用压缩:', props.enableCompress, 
            '文件大小:', fileSizeMB.toFixed(2), 'MB', 
            '压缩阈值:', props.compressSize, 'MB');
            
          // 执行压缩
          const compressedFile = await compressImage(file.raw, {
            maxSize: props.maxSize,
            compressSize: props.compressSize,
            enableCompress: props.enableCompress,
            compressQuality: props.compressQuality,
            maxCompressTries: props.maxCompressTries
          });
          
          // 关闭提示
          loadingMsg.close();
          
          // 获取最新的文件列表，可能在压缩过程中已经变化
          const latestFileList = getFileList();
          
          // 找到当前文件在列表中的索引
          const fileIndex = latestFileList.findIndex(f => f.uid === file.uid);
          
          if (fileIndex !== -1) {
            // 更新文件对象
            const updatedFile = {
              ...latestFileList[fileIndex],
              size: compressedFile.size,
              raw: compressedFile,
              status: 'ready' // 恢复为ready状态
            };
            
            // 更新文件列表
            latestFileList[fileIndex] = updatedFile;
            
            // 转换为base64用于预览
            await convertFileToBase64(updatedFile);
            
            // 更新状态
            emit('update:modelValue', latestFileList);
            emit('change', updatedFile, latestFileList);
            
            return;
          } else {
            // 文件不在列表中，尝试重新添加
            console.warn('压缩完成但文件已不在列表中，重新添加:', file.name);
            const updatedFile = {
              ...file,
              size: compressedFile.size,
              raw: compressedFile,
              status: 'ready'
            };
            
            // 转换为base64用于预览
            await convertFileToBase64(updatedFile);
            
            // 更新文件列表
            latestFileList.push(updatedFile);
            emit('update:modelValue', latestFileList);
            emit('change', updatedFile, latestFileList);
            
            return;
          }
        } catch (err) {
          // 关闭提示
          loadingMsg.close();
          
          // 避免显示重复的"文件大小超过"错误提示
          const errorMessage = err.message && err.message.includes('图片大小不能超过') ? 
            '图片压缩失败' : `图片压缩失败: ${err.message}`;
          ElMessage.error(errorMessage);
          console.error('压缩失败:', err);
          
          // 从文件列表中移除这个文件
          const latestFileList = getFileList();
          const updatedFileList = latestFileList.filter(f => f.uid !== file.uid);
          emit('update:modelValue', updatedFileList);
          emit('error', err, file);
          emit('change', file, updatedFileList);
          return;
        }
      } else {
        // 不需要压缩，只转换为base64
        console.log('跳过压缩，直接处理:', file.name, '大小:', fileSizeMB.toFixed(2), 'MB');
        
        // 将状态设置为 uploading
        file.status = 'uploading';
        
        // 转换为base64用于预览，同时确保文件原始数据可用于上传
        await convertFileToBase64(file);
        
        // 注意：文件处理完成后，后续的上传将由 el-upload 组件通过 customHttpRequest 处理
        // 确保这里只进行初步处理，实际URL更新应由 customHttpRequest 完成
        
        // 获取最新的文件列表
        const latestFileList = getFileList();
        
        // 找到当前文件在列表中的索引
        const fileIndex = latestFileList.findIndex(f => f.uid === file.uid);
        
        if (fileIndex !== -1) {
          // 文件存在，更新状态为 ready，等待上传
          latestFileList[fileIndex] = { 
            ...latestFileList[fileIndex],
            status: 'ready' // 恢复为ready状态，使得el-upload能够处理上传 
          };
          
          // 更新文件列表
          emit('update:modelValue', latestFileList);
          emit('change', file, latestFileList);
          
          return;
        } else {
          // 文件不在列表中，添加回去
          console.warn('文件不在列表中，重新添加:', file.name);
          file.status = 'ready';
          latestFileList.push(file);
          
          // 更新文件列表
          emit('update:modelValue', latestFileList);
          emit('change', file, latestFileList);
          
          return;
        }
      }
    } catch (err) {
      console.error('处理文件失败:', err);
      emit('error', err, file);
      
      // 移除处理失败的文件
      const latestFileList = getFileList();
      const updatedFileList = latestFileList.filter(f => f.uid !== file.uid);
      emit('update:modelValue', updatedFileList);
      return;
    }
  }
  
  // 确保所有fileList中的文件都有base64属性
  const processPromises = [];
  currentFileList.forEach(item => {
    if (item.raw && !item.base64 && item.raw instanceof File) {
      processPromises.push(convertFileToBase64(item));
    }
  });
  
  if (processPromises.length > 0) {
    try {
      await Promise.all(processPromises);
    } catch (err) {
      console.error('处理文件列表失败:', err);
    }
  }
  
  // 更新文件列表，确保保持一致的数据格式
  const formattedData = formatOutputData(currentFileList, props.dataFormat, initialDataFormat.value);
  emit('update:modelValue', formattedData);
  
  // 始终发出change事件，确保父组件可以接收到事件
  console.log('发出最终change事件:', file?.name, currentFileList.length, currentFileList);
  emit('change', file, currentFileList);
};

// 获取当前文件列表中的URL数组
const getFileUrls = () => {
  return getFileUrlsHelper(getFileList());
};

// 获取el-upload组件的引用
const uploadRef = ref(null);

// 手动上传所有文件
const uploadFiles = () => {
  console.log('手动上传所有文件');
  
  if (!uploadRef.value) {
    console.warn('上传组件不存在');
    return;
  }
  
  const filesToUpload = getFileList().filter(file => 
    file.status !== 'success' && file.raw instanceof File
  );
  
  if (filesToUpload.length === 0) {
    ElMessage.info('没有需要上传的文件');
    return;
  }
  
  // 使用el-upload的submit方法执行上传
  uploadRef.value.submit();
};

// 上传单个文件
const upload = (file) => {
  if (!file) {
    console.warn('上传失败：未指定文件');
    return;
  }
  
  if (!uploadRef.value) {
    console.warn('上传组件不存在');
    return;
  }
  
  // 查找文件对象
  const targetFile = getFileList().find(f => f.uid === file.uid);
  
  if (targetFile) {
    // 上传指定文件
    uploadRef.value.submit(targetFile);
  } else {
    console.warn('上传失败：找不到指定的文件', file);
  }
};

// 清空文件列表
const clearFiles = () => {
  console.log('清空文件列表');
  const formattedData = props.dataFormat === 'string' ? [] : [];
  emit('update:modelValue', formattedData);
};

// 暴露方法给父组件
defineExpose({
  uploadFiles,
  upload,
  getFileUrls,
  clearFiles
});

// 处理移除图片
const handleImageRemove = (file, fileList) => {
  console.log('handleImageRemove 被调用:', file.name, fileList?.length || 0);
  
  // 确保fileList是数组
  const safeFileList = Array.isArray(fileList) ? fileList : [];
  
  // 确保文件被正确移除
  const updatedFileList = safeFileList.filter(f => f.uid !== file.uid);
  
  // 更新文件列表，保持格式一致性
  const formattedData = formatOutputData(updatedFileList, props.dataFormat, initialDataFormat.value);
  emit('update:modelValue', formattedData);
  emit('remove', file, updatedFileList);
  
  // 创建简单的URL数组并发送
  const urlList = updatedFileList
    .filter(f => f.status === 'success' && f.url)
    .map(f => f.url);
  
  // 发出额外的URL列表更新事件
  emit('urlsChange', urlList);
};

// 处理超出上传限制
const handleImageExceed = (files, fileList) => {
  ElMessage.warning(`当前限制选择 ${props.limit} 个文件，本次选择了 ${files.length} 个文件，共选择了 ${files.length + fileList.length} 个文件`);
  emit('exceed', files, fileList);
};

// 处理上传成功事件
const handleUploadSuccess = (response, file, fileList) => {
  console.log('handleUploadSuccess被调用:', response, file?.name);
  
  // 确保响应有效
  if (!response || !response.data) {
    console.warn('上传响应无效', response);
    return;
  }

  // 获取服务器返回的URL
  const serverUrl = response.data;
  
  // 根据uid查找文件
  const currentFileList = getFileList();
  const fileIndex = currentFileList.findIndex(f => f.uid === file.uid);
  
  if (fileIndex !== -1) {
    // 明确更新文件的URL和状态
    const updatedFile = {
      ...currentFileList[fileIndex],
      url: serverUrl,
      status: 'success',
      response
    };
    
    console.log(`明确更新图片URL从[${currentFileList[fileIndex].url || '无'}]到[${serverUrl}]`);
    
    // 更新文件列表中的文件
    currentFileList[fileIndex] = updatedFile;
    
    // 更新文件列表
    emit('update:modelValue', formatOutputData(currentFileList, props.dataFormat, initialDataFormat.value));
    
    // 创建简单的URL数组并发送urlsChange事件
    const urlList = currentFileList
      .filter(f => (f.status === 'success' || f.url === serverUrl) && f.url)
      .map(f => f.url);
    
    // 发出额外的URL列表更新事件
    emit('urlsChange', urlList);
    
    // 触发change事件
    emit('change', updatedFile, currentFileList);
  } else {
    console.warn('无法在当前文件列表中找到上传的文件', file);
  }
};
</script>

<style lang="scss" scoped>
.image-upload-component {
  :deep(.el-upload--picture-card) {
    width: 100px;
    height: 100px;
    line-height: 100px;
  }

  :deep(.el-upload-list--picture-card .el-upload-list__item) {
    width: 100px;
    height: 100px;
  }
  
  :deep(.el-upload__tip) {
    margin-top: 8px;
    line-height: 1.2;
    padding-left: 5px;
  }
  
  :deep(.el-upload-dragger) {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    
    .el-icon--upload {
      font-size: 24px;
      color: #c0c4cc;
      margin-bottom: 5px;
    }
    
    .el-upload__text {
      font-size: 12px;
      color: #909399;
      
      em {
        color: #409eff;
        font-style: normal;
      }
    }
  }
  
  :deep(.el-upload-list--picture-card .el-upload-list__item-thumbnail) {
    object-fit: v-bind('props.imageScale');
  }
}
</style> 